namespace Mahjong
{
    /****************************************************************************
     Copyright (c) 2016-2020 Jeff Wang <summer_insects@163.com>

     Permission is hereby granted, free of charge, to any person obtaining a copy
     of this software and associated documentation files (the "Software"), to deal
     in the Software without restriction, including without limitation the rights
     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     copies of the Software, and to permit persons to whom the Software is
     furnished to do so, subject to the following conditions:

     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.

     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     SOFTWARE.
     ****************************************************************************/

    /// <summary>
    /// 算番流程概述：
    /// 1. 判断特殊和型
    ///   (1) 门清状态——七对、十三幺、组合龙、全不靠
    ///   (2) 1副露状态——组合龙
    /// 2. 按基本和型划分（留意七对）
    /// 3. 对第2步中的划分结果进行算番，取最大值
    /// </summary>
    /// <summary>
    /// 对划分后的结果算番流程：
    /// 1. 计数顺子个数和刻子个数，对刻子自身算番（涉及：幺九刻、箭刻）
    /// 2. 分情况对面子算番
    ///   (1) 4顺——先判断三色/一色双龙会，没有再计算4顺的番
    ///   (2) 3顺1刻——计算3顺的番
    ///   (3) 2顺2刻——计算2顺的番，计算2刻的番
    ///   (4) 1顺3刻——计算3刻的番
    /// 3. 九莲宝灯判断
    /// 4. 根据和牌方式调整——涉及：不求人、全求人
    /// 5. 根据雀头调整——涉及：平和、小三元、小四喜
    /// 6. 根据牌组特征调整——涉及：全带幺、全带五、全双刻
    /// 7. 根据花色调整——涉及：无字、缺一门、混一色、清一色、五门齐
    /// 8. 根据牌特性调整——涉及：断幺、推不倒、绿一色、字一色、清幺九、混幺九
    /// 9. 根据数牌的范围调整——涉及：大于五、小于五、全大、全中、全小
    /// 10. 计算四归一
    /// 11. 根据听牌方式调整——涉及：边张、嵌张、单钓将
    /// 12. 统一调整规则中规定不计的
    /// 13. 最后调整圈风门风
    /// 14. 以上流程走完，得到算番结果。如果为0番，则调整为无番和
    /// </summary>
    public class FanCalculator
    {
        private const int MAX_DIVISION_CNT = 20; // 一副牌最多也没有20种划分吧，够用了

        // 牌类型
        public enum PackType
        {
            Chow = 0,    // 顺子
            Pung = 1,    // 刻子
            Kong = 2,    // 杠
            Pair = 3     // 雀头
        }

        // 风位
        public enum Wind
        {
            East = 0,
            South = 1,
            West = 2,
            North = 3
        }

        // 和牌标记
        [Flags]
        public enum WinFlag
        {
            None = 0,
            SelfDrawn = 2,           // 自摸
            WallLast = 4,            // 海底捞月
            _4thTile = 8,             // 和绝张
            AboutKong = 16,          // 与杠有关
        }

        private enum FanType
        {
            None = 0,                       ///< 无效
            BigFourWinds = 1,               ///< 大四喜
            BigThreeDragons = 2,            ///< 大三元
            AllGreen = 3,                   ///< 绿一色
            NineGates = 4,                  ///< 九莲宝灯
            FourKongs = 5,                  ///< 四杠
            SevenShiftedPairs = 6,          ///< 连七对
            ThirteenOrphans = 7,            ///< 十三幺

            AllTerminals = 8,               ///< 清幺九
            LittleFourWinds = 9,            ///< 小四喜
            LittleThreeDragons = 10,        ///< 小三元
            AllHonors = 11,                 ///< 字一色
            FourConcealedPungs = 12,        ///< 四暗刻
            PureTerminalChows = 13,         ///< 一色双龙会

            QuadrupleChow = 14,             ///< 一色四同顺
            FourPureShiftedPungs = 15,      ///< 一色四节高

            FourPureShiftedChows = 16,      ///< 一色四步高
            ThreeKongs = 17,                ///< 三杠
            AllTerminalsAndHonors = 18,     ///< 混幺九

            SevenPairs = 19,                ///< 七对
            GreaterHonorsAndKnittedTiles = 20, ///< 七星不靠
            AllEvenPungs = 21,              ///< 全双刻
            FullFlush = 22,                 ///< 清一色
            PureTripleChow = 23,            ///< 一色三同顺
            PureShiftedPungs = 24,          ///< 一色三节高
            UpperTiles = 25,                ///< 全大
            MiddleTiles = 26,               ///< 全中
            LowerTiles = 27,                ///< 全小

            PureStraight = 28,              ///< 清龙
            ThreeSuitedTerminalChows = 29,  ///< 三色双龙会
            PureShiftedChows = 30,          ///< 一色三步高
            AllFive = 31,                   ///< 全带五
            TriplePung = 32,                ///< 三同刻
            ThreeConcealedPungs = 33,       ///< 三暗刻

            LesserHonorsAndKnittedTiles = 34, ///< 全不靠
            KnittedStraight = 35,           ///< 组合龙
            UpperFour = 36,                 ///< 大于五
            LowerFour = 37,                 ///< 小于五
            BigThreeWinds = 38,             ///< 三风刻

            MixedStraight = 39,             ///< 花龙
            ReversibleTiles = 40,           ///< 推不倒
            MixedTripleChow = 41,           ///< 三色三同顺
            MixedShiftedPungs = 42,         ///< 三色三节高
            ChickenHand = 43,               ///< 无番和
            LastTileDraw = 44,              ///< 妙手回春
            LastTileClaim = 45,             ///< 海底捞月
            OutWithReplacementTile = 46,    ///< 杠上开花
            RobbingTheKong = 47,            ///< 抢杠和

            AllPungs = 48,                  ///< 碰碰和
            HalfFlush = 49,                 ///< 混一色
            MixedShiftedChows = 50,         ///< 三色三步高
            AllTypes = 51,                  ///< 五门齐
            MeldedHand = 52,                ///< 全求人
            TwoConcealedKongs = 53,         ///< 双暗杠
            TwoDragonsPungs = 54,           ///< 双箭刻

            OutsideHand = 55,               ///< 全带幺
            FullyConcealedHand = 56,        ///< 不求人
            TwoMeldedKongs = 57,            ///< 双明杠
            LastTile = 58,                  ///< 和绝张

            DragonPung = 59,                ///< 箭刻
            PrevalentWind = 60,             ///< 圈风刻
            SeatWind = 61,                  ///< 门风刻
            ConcealedHand = 62,             ///< 门前清
            AllChows = 63,                  ///< 平和
            TileHog = 64,                   ///< 四归一
            DoublePung = 65,                ///< 双同刻
            TwoConcealedPungs = 66,         ///< 双暗刻
            ConcealedKong = 67,             ///< 暗杠
            AllSimples = 68,                ///< 断幺

            PureDoubleChow = 69,            ///< 一般高
            MixedDoubleChow = 70,           ///< 喜相逢
            ShortStraight = 71,             ///< 连六
            TwoTerminalChows = 72,          ///< 老少副
            PungOfTerminalsOrHonors = 73,   ///< 幺九刻
            MeldedKong = 74,                ///< 明杠
            OneVoidedSuit = 75,             ///< 缺一门
            NoHonors = 76,                  ///< 无字
            EdgeWait = 77,                  ///< 边张
            ClosedWait = 78,                ///< 嵌张
            SingleWait = 79,                ///< 单钓将
            SelfDrawn = 80,                 ///< 自摸

            FlowerTiles = 81,               ///< 花牌

            ConcealedKongAndMeldedKong = 82, ///< 明暗杠
        }

        // 番值表保持不变
        private static readonly int[] FanValueTable = new int[]
        {
            0,  // None
            88, // BigFourWinds
            88, // BigThreeDragons
            88, // AllGreen
            88, // NineGates
            88, // FourKongs
            88, // SevenShiftedPairs
            88, // ThirteenOrphans

            64, // AllTerminals
            64, // LittleFourWinds
            64, // LittleThreeDragons
            64, // AllHonors
            64, // FourConcealedPungs
            64, // PureTerminalChows

            48, // QuadrupleChow
            48, // FourPureShiftedPungs

            32, // FourPureShiftedChows
            32, // ThreeKongs
            32, // AllTerminalsAndHonors

            24, // SevenPairs
            24, // GreaterHonorsAndKnittedTiles
            24, // AllEvenPungs
            24, // FullFlush
            24, // PureTripleChow
            24, // PureShiftedPungs
            24, // UpperTiles
            24, // MiddleTiles
            24, // LowerTiles

            16, // PureStraight
            16, // ThreeSuitedTerminalChows
            16, // PureShiftedChows
            16, // AllFive
            16, // TriplePung
            16, // ThreeConcealedPungs

            12, // LesserHonorsAndKnittedTiles
            12, // KnittedStraight
            12, // UpperFour
            12, // LowerFour
            12, // BigThreeWinds

            8,  // MixedStraight
            8,  // ReversibleTiles
            8,  // MixedTripleChow
            8,  // MixedShiftedPungs
            8,  // ChickenHand
            8,  // LastTileDraw
            8,  // LastTileClaim
            8,  // OutWithReplacementTile
            8,  // RobbingTheKong

            6,  // AllPungs
            6,  // HalfFlush
            6,  // MixedShiftedChows
            6,  // AllTypes
            6,  // MeldedHand
            6,  // TwoConcealedKongs
            6,  // TwoDragonsPungs

            4,  // OutsideHand
            4,  // FullyConcealedHand
            4,  // TwoMeldedKongs
            4,  // LastTile

            2,  // DragonPung
            2,  // PrevalentWind
            2,  // SeatWind
            2,  // ConcealedHand
            2,  // AllChows
            2,  // TileHog
            2,  // DoublePung
            2,  // TwoConcealedPungs
            2,  // ConcealedKong
            2,  // AllSimples

            1,  // PureDoubleChow
            1,  // MixedDoubleChow
            1,  // ShortStraight
            1,  // TwoTerminalChows
            1,  // PungOfTerminalsOrHonors
            1,  // MeldedKong
            1,  // OneVoidedSuit
            1,  // NoHonors
            1,  // EdgeWait
            1,  // ClosedWait
            1,  // SingleWait
            1,  // SelfDrawn

            1,  // FlowerTiles

            5,  // ConcealedKongAndMeldedKong
        };

        // 标准牌
        private static readonly int[] AllTiles = new int[]
        {
            0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,  // 万1-9
            0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,  // 条1-9
            0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,  // 饼1-9
            0x41, 0x42, 0x43, 0x44,                              // 东南西北
            0x45, 0x46, 0x47                               // 中发白
        };

        // 标准组合龙
        private static readonly int[][] StandardKnittedStraight = new int[][]
        {
            new int[] { 0x11, 0x14, 0x17, 0x22, 0x25, 0x28, 0x33, 0x36, 0x39 },
            new int[] { 0x11, 0x14, 0x17, 0x23, 0x26, 0x29, 0x32, 0x35, 0x38 },
            new int[] { 0x12, 0x15, 0x18, 0x21, 0x24, 0x27, 0x33, 0x36, 0x39 },
            new int[] { 0x12, 0x15, 0x18, 0x23, 0x26, 0x29, 0x31, 0x34, 0x37 },
            new int[] { 0x13, 0x16, 0x19, 0x21, 0x24, 0x27, 0x32, 0x35, 0x38 },
            new int[] { 0x13, 0x16, 0x19, 0x22, 0x25, 0x28, 0x31, 0x34, 0x37 }
        };

        // 标准十三幺
        private static readonly int[] StandardThirteenOrphans = new int[]
        {
            0x11, 0x19, 0x21, 0x29, 0x31, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47
        };

        // 牌组结构
        public struct Pack : IComparable
        {
            public int Tile { get; set; }
            public PackType Type { get; set; }
            public bool IsMelded { get; set; }

            public Pack(int tile, PackType type, bool isMelded = false)
            {
                Tile = tile;
                Type = type;
                IsMelded = isMelded;
            }

            int Convert()
            {
                return (int)Type * 10000 + (IsMelded ? 1000 : 0) + Tile;
            }

            public int CompareTo(object obj)
            {
                return Convert() - ((Pack)obj).Convert();
            }
        }

        // 手牌结构
        public class HandTiles
        {
            public Pack[] FixedPacks { get; set; }
            public int PackCount { get; set; }
            public int[] StandingTiles { get; set; }
            public int TileCount { get; set; }

            public HandTiles()
            {
                FixedPacks = new Pack[4];
                StandingTiles = new int[13];
                PackCount = 0;
                TileCount = 0;
            }
        }

        // 计算参数结构
        private class CalculateParam
        {
            public HandTiles HandTiles { get; set; }
            public int WinTile { get; set; }
            public WinFlag WinFlag { get; set; }
            public Wind PrevalentWind { get; set; }
            public Wind SeatWind { get; set; }
            public int FlowerCount { get; set; }
        }

        // 划分结构
        private class Division
        {
            public Pack[] Packs { get; set; }

            public Division()
            {
                Packs = new Pack[5];
            }
        }

        // 划分结果结构
        private class DivisionResult
        {
            public Division[] Divisions { get; set; }
            public int Count { get; set; }

            public DivisionResult()
            {
                Divisions = new Division[MAX_DIVISION_CNT];
                Count = 0;
                for (int i = 0; i < MAX_DIVISION_CNT; i++)
                {
                    Divisions[i] = new Division();
                }
            }
        }

        // 映射手牌到牌表
        private static bool MapHandTiles(HandTiles handTiles, int[] cntTable)
        {
            Array.Clear(cntTable, 0, cntTable.Length);

            // 处理副露牌
            for (int i = 0; i < handTiles.PackCount; i++)
            {
                Pack pack = handTiles.FixedPacks[i];
                switch (pack.Type)
                {
                    case PackType.Chow:
                        cntTable[pack.Tile - 1]++;
                        cntTable[pack.Tile]++;
                        cntTable[pack.Tile + 1]++;
                        break;
                    case PackType.Pung:
                    case PackType.Kong:
                        cntTable[pack.Tile] += 3;
                        break;
                }
            }

            // 处理立牌
            for (int i = 0; i < handTiles.TileCount; i++)
            {
                cntTable[handTiles.StandingTiles[i]]++;
            }

            // 检查牌数是否正确
            int totalCount = 0;
            for (int i = 0; i < cntTable.Length; i++)
            {
                totalCount += cntTable[i];
            }

            return totalCount == 13;
        }

        // 映射立牌到牌表
        private static void MapTiles(int[] standingTiles, int standingCnt, int[] cntTable)
        {
            Array.Clear(cntTable, 0, cntTable.Length);
            for (int i = 0; i < standingCnt; i++)
            {
                cntTable[standingTiles[i]]++;
            }
        }

        // 判断是否为序数牌
        private static bool IsNumberedSuit(int tile)
        {
            return tile >= 0x11 && tile <= 0x39;
        }

        // 判断是否为字牌
        private static bool IsHonor(int tile)
        {
            return tile >= 0x41 && tile <= 0x47;
        }

        // 判断是否为风牌
        private static bool IsWinds(int tile)
        {
            return tile >= 0x41 && tile <= 0x44;
        }

        // 判断是否为箭牌
        private static bool IsDragons(int tile)
        {
            return tile >= 0x45 && tile <= 0x47;
        }

        // 判断是否为幺九牌或字牌
        private static bool IsTerminalOrHonor(int tile)
        {
            if (IsHonor(tile)) return true;
            if (!IsNumberedSuit(tile)) return false;

            int rank = GetRank(tile);
            return rank == 1 || rank == 9;
        }

        // 判断是否为幺九牌
        private static bool IsTerminal(int tile)
        {
            if (!IsNumberedSuit(tile)) return false;
            int rank = GetRank(tile);
            return rank == 1 || rank == 9;
        }

        // 判断是否为绿牌
        private static bool IsGreen(int tile)
        {
            return tile == 0x22 || tile == 0x23 || tile == 0x24 || tile == 0x26 || tile == 0x28 || tile == 0x46;
        }

        // 判断是否为可推倒牌
        private static bool IsReversible(int tile)
        {
            return tile == 0x22 || tile == 0x24 || tile == 0x25 || tile == 0x26 || tile == 0x28 || tile == 0x29 || tile == 0x31 || tile == 0x32 || tile == 0x33 || tile == 0x34 || tile == 0x35 || tile == 0x38 || tile == 0x39 || tile == 0x47;
        }

        // 获取牌的花色
        private static int GetSuit(int tile)
        {
            if (tile >= 0x11 && tile <= 0x19) return 1; // 万
            if (tile >= 0x21 && tile <= 0x29) return 2; // 条
            if (tile >= 0x31 && tile <= 0x39) return 3; // 饼
            return 4; // 字牌
        }

        // 获取牌的数字
        private static int GetRank(int tile)
        {
            if (IsHonor(tile)) return 0;
            return tile & 0x0F;
        }

        // 判断花色是否相同（快速）
        private static bool IsSuitEqualQuick(int tile1, int tile2)
        {
            return GetSuit(tile1) == GetSuit(tile2);
        }

        // 判断数字是否相同（快速）
        private static bool IsRankEqualQuick(int tile1, int tile2)
        {
            return GetRank(tile1) == GetRank(tile2);
        }

        // 判断是否为序数牌（快速）
        private static bool IsNumberedSuitQuick(int tile)
        {
            return tile >= 0x11 && tile <= 0x39;
        }

        // 牌组转牌
        private static int PacksToTiles(Pack[] packs, int packCnt, int[] tiles, int handCnt)
        {
            int tileCnt = handCnt;
            for (int i = 0; i < packCnt; i++)
            {
                Pack pack = packs[i];
                switch (pack.Type)
                {
                    case PackType.Chow:
                        tiles[tileCnt++] = pack.Tile - 1;
                        tiles[tileCnt++] = pack.Tile;
                        tiles[tileCnt++] = pack.Tile + 1;
                        break;
                    case PackType.Pung:
                    case PackType.Kong:
                        tiles[tileCnt++] = pack.Tile;
                        tiles[tileCnt++] = pack.Tile;
                        tiles[tileCnt++] = pack.Tile;
                        break;
                    case PackType.Pair:
                        tiles[tileCnt++] = pack.Tile;
                        tiles[tileCnt++] = pack.Tile;
                        break;
                }
            }
            return tileCnt;
        }

        // 牌表转牌
        private static int TableToTiles(int[] cntTable, int[] tiles, int maxSize)
        {
            int tileCnt = 0;
            for (int i = 0; i < cntTable.Length && tileCnt < maxSize; i++)
            {
                for (int j = 0; j < cntTable[i] && tileCnt < maxSize; j++)
                {
                    tiles[tileCnt++] = i;
                }
            }
            return tileCnt;
        }

        // 递归划分的最后一步，添加划分
        private static void DivideTailAddDivision(int fixedCnt, Division workDivision, DivisionResult result)
        {
            // 拷贝一份当前的划分出来的面子，并排序暗手的面子
            Division temp = new Division();
            Array.Copy(workDivision.Packs, temp.Packs, 5);

            // 排序面子（不包括雀头）
            Array.Sort(temp.Packs, fixedCnt, 4 - fixedCnt);

            // 检查这种划分是否已经存在
            bool exists = false;
            for (int i = 0; i < result.Count; i++)
            {
                bool equal = true;
                for (int j = fixedCnt; j < 4; j++)
                {
                    if (temp.Packs[j].Tile != result.Divisions[i].Packs[j].Tile ||
                        temp.Packs[j].Type != result.Divisions[i].Packs[j].Type ||
                        temp.Packs[j].IsMelded != result.Divisions[i].Packs[j].IsMelded)
                    {
                        equal = false;
                        break;
                    }
                }
                if (equal)
                {
                    exists = true;
                    break;
                }
            }

            if (!exists && result.Count < MAX_DIVISION_CNT)
            {
                // 写入划分结果
                result.Divisions[result.Count] = temp;
                result.Count++;
            }
        }

        // 递归划分的最后一步
        private static bool DivideTail(int[] cntTable, int fixedCnt, Division workDivision, DivisionResult result)
        {
            for (int i = 0; i < 34; i++)
            {
                int t = AllTiles[i];
                if (cntTable[t] < 2)
                    continue;

                cntTable[t] -= 2; // 削减

                // 所有牌全部使用完毕
                bool allUsed = true;
                for (int j = 0; j < cntTable.Length; j++)
                {
                    if (cntTable[j] != 0)
                    {
                        allUsed = false;
                        break;
                    }
                }

                if (allUsed)
                {
                    cntTable[t] += 2; // 还原

                    // 这2张作为雀头
                    workDivision.Packs[4] = new Pack(t, PackType.Pair);
                    DivideTailAddDivision(fixedCnt, workDivision, result); // 记录
                    return true;
                }

                cntTable[t] += 2; // 还原
            }

            return false;
        }

        // 判断一条划分分支是否来过
        private static bool IsDivisionBranchExist(int fixedCnt, int step, Division workDivision, DivisionResult result)
        {
            // 没有划分时，以及划分步骤小于3时，不检测，因为至少要有3步递归才会产生不同划分
            if (result.Count <= 0 || step < 3)
                return false;

            // 创建临时划分用于比较
            Division temp = new Division();
            Array.Copy(workDivision.Packs, fixedCnt, temp.Packs, fixedCnt, step);

            // 排序面子
            Array.Sort(temp.Packs, fixedCnt, step);

            // 检查是否存在相同分支
            for (int i = 0; i < result.Count; i++)
            {
                bool includes = true;
                for (int j = 0; j < step; j++)
                {
                    bool found = false;
                    for (int k = fixedCnt; k < 4; k++)
                    {
                        if (temp.Packs[fixedCnt + j].Tile == result.Divisions[i].Packs[k].Tile &&
                            temp.Packs[fixedCnt + j].Type == result.Divisions[i].Packs[k].Type &&
                            temp.Packs[fixedCnt + j].IsMelded == result.Divisions[i].Packs[k].IsMelded)
                        {
                            found = true;
                            break;
                        }
                    }
                    if (!found)
                    {
                        includes = false;
                        break;
                    }
                }
                if (includes)
                    return true;
            }

            return false;
        }

        // 递归划分
        private static bool DivideRecursively(int[] cntTable, int fixedCnt, int step, Division workDivision, DivisionResult result)
        {
            int idx = step + fixedCnt;
            if (idx == 4) // 4组面子都有了
            {
                return DivideTail(cntTable, fixedCnt, workDivision, result);
            }

            bool ret = false;

            // 按牌表张遍历牌
            for (int i = 0; i < 34; i++)
            {
                int t = AllTiles[i];
                if (cntTable[t] < 1)
                    continue;

                // 刻子
                if (cntTable[t] > 2)
                {
                    workDivision.Packs[idx] = new Pack(t, PackType.Pung); // 记录刻子
                    if (!IsDivisionBranchExist(fixedCnt, step + 1, workDivision, result))
                    {
                        // 削减这组刻子，递归
                        cntTable[t] -= 3;
                        if (DivideRecursively(cntTable, fixedCnt, step + 1, workDivision, result))
                        {
                            ret = true;
                        }
                        // 还原
                        cntTable[t] += 3;
                    }
                }

                // 顺子（只能是数牌）
                if (IsNumberedSuit(t))
                {
                    int rank = GetRank(t);
                    if (rank < 8 && cntTable[t + 1] > 0 && cntTable[t + 2] > 0)
                    {
                        workDivision.Packs[idx] = new Pack(t + 1, PackType.Chow); // 记录顺子
                        if (!IsDivisionBranchExist(fixedCnt, step + 1, workDivision, result))
                        {
                            // 削减这组顺子，递归
                            cntTable[t]--;
                            cntTable[t + 1]--;
                            cntTable[t + 2]--;
                            if (DivideRecursively(cntTable, fixedCnt, step + 1, workDivision, result))
                            {
                                ret = true;
                            }
                            // 还原
                            cntTable[t]++;
                            cntTable[t + 1]++;
                            cntTable[t + 2]++;
                        }
                    }
                }
            }

            return ret;
        }

        // 划分一手牌
        private static bool DivideWinHand(int[] standingTiles, Pack[] fixedPacks, int fixedCnt, DivisionResult result)
        {
            int standingCnt = 14 - fixedCnt * 3;

            // 对立牌的种类进行打表
            int[] cntTable = new int[136];
            MapTiles(standingTiles, standingCnt, cntTable);

            result.Count = 0;

            // 复制副露的面子
            Division workDivision = new Division();
            for (int i = 0; i < fixedCnt; i++)
            {
                workDivision.Packs[i] = fixedPacks[i];
            }

            return DivideRecursively(cntTable, fixedCnt, 0, workDivision, result);
        }

        // 4组递增1
        private static bool IsFourShifted1(int r0, int r1, int r2, int r3)
        {
            return (r0 + 1 == r1 && r1 + 1 == r2 && r2 + 1 == r3);
        }

        // 4组递增2
        private static bool IsFourShifted2(int r0, int r1, int r2, int r3)
        {
            return (r0 + 2 == r1 && r1 + 2 == r2 && r2 + 2 == r3);
        }

        // 3组递增1
        private static bool IsShifted1(int r0, int r1, int r2)
        {
            return (r0 + 1 == r1 && r1 + 1 == r2);
        }

        // 3组递增2
        private static bool IsShifted2(int r0, int r1, int r2)
        {
            return (r0 + 2 == r1 && r1 + 2 == r2);
        }

        // 三色
        private static bool IsMixed(int s0, int s1, int s2)
        {
            return (s0 != s1 && s0 != s2 && s1 != s2);
        }

        // 3组递增1无序
        private static bool IsShifted1Unordered(int r0, int r1, int r2)
        {
            return IsShifted1(r1, r0, r2) || IsShifted1(r2, r0, r1) || IsShifted1(r0, r1, r2) ||
                   IsShifted1(r2, r1, r0) || IsShifted1(r0, r2, r1) || IsShifted1(r1, r2, r0);
        }

        // 4组顺子的番
        private static FanType Get4ChowsFan(int t0, int t1, int t2, int t3)
        {
            // 按出现频率顺序

            // 一色四步高
            if (IsFourShifted2(t0, t1, t2, t3) || IsFourShifted1(t0, t1, t2, t3))
            {
                return FanType.FourPureShiftedChows;
            }
            // 一色四同顺
            if (t0 == t1 && t0 == t2 && t0 == t3)
            {
                return FanType.QuadrupleChow;
            }
            // 以上都没有
            return FanType.None;
        }

        // 3组顺子的番
        private static FanType Get3ChowsFan(int t0, int t1, int t2)
        {
            int s0 = GetSuit(t0);
            int s1 = GetSuit(t1);
            int s2 = GetSuit(t2);

            int r0 = GetRank(t0);
            int r1 = GetRank(t1);
            int r2 = GetRank(t2);

            // 按出现频率顺序

            if (IsMixed(s0, s1, s2)) // 三色
            {
                // 三色三步高
                if (IsShifted1Unordered(r1, r0, r2))
                {
                    return FanType.MixedShiftedChows;
                }
                // 三色三同顺
                if (r0 == r1 && r1 == r2)
                {
                    return FanType.MixedTripleChow;
                }
                // 花龙
                if ((r0 == 2 && r1 == 5 && r2 == 8) || (r0 == 2 && r1 == 8 && r2 == 5) ||
                    (r0 == 5 && r1 == 2 && r2 == 8) || (r0 == 5 && r1 == 8 && r2 == 2) ||
                    (r0 == 8 && r1 == 2 && r2 == 5) || (r0 == 8 && r1 == 5 && r2 == 2))
                {
                    return FanType.MixedStraight;
                }
            }
            else // 一色
            {
                // 清龙
                if (t0 + 3 == t1 && t1 + 3 == t2)
                {
                    return FanType.PureStraight;
                }
                // 一色三步高
                if (IsShifted2(t0, t1, t2) || IsShifted1(t0, t1, t2))
                {
                    return FanType.PureShiftedChows;
                }
                // 一色三同顺
                if (t0 == t1 && t0 == t2)
                {
                    return FanType.PureTripleChow;
                }
            }
            // 以上都没有
            return FanType.None;
        }

        // 2组顺子的番
        private static FanType Get2ChowsFanUnordered(int t0, int t1)
        {
            // 按出现频率顺序

            if (!IsSuitEqualQuick(t0, t1)) // 两色
            {
                // 喜相逢
                if (IsRankEqualQuick(t0, t1))
                {
                    return FanType.MixedDoubleChow;
                }
            }
            else // 一色
            {
                // 连六
                if (t0 + 3 == t1 || t1 + 3 == t0)
                {
                    return FanType.ShortStraight;
                }
                // 老少副
                int r0 = GetRank(t0);
                int r1 = GetRank(t1);
                if ((r0 == 2 && r1 == 8) || (r0 == 8 && r1 == 2))
                {
                    return FanType.TwoTerminalChows;
                }
                // 一般高
                if (t0 == t1)
                {
                    return FanType.PureDoubleChow;
                }
            }
            // 以上都没有
            return FanType.None;
        }

        // 4组刻子的番
        private static FanType Get4PungsFan(int t0, int t1, int t2, int t3)
        {
            // 一色四节高
            if (IsNumberedSuitQuick(t0) && t0 + 1 == t1 && t1 + 1 == t2 && t2 + 1 == t3)
            {
                return FanType.FourPureShiftedPungs;
            }
            // 大四喜
            if (t0 == 0x41 && t1 == 0x42 && t2 == 0x43 && t3 == 0x44)
            {
                return FanType.BigFourWinds;
            }
            // 以上都没有
            return FanType.None;
        }

        // 3组刻子的番
        private static FanType Get3PungsFan(int t0, int t1, int t2)
        {
            // 按出现频率顺序

            if (IsNumberedSuitQuick(t0) && IsNumberedSuitQuick(t1) && IsNumberedSuitQuick(t2)) // 数牌
            {
                int s0 = GetSuit(t0);
                int s1 = GetSuit(t1);
                int s2 = GetSuit(t2);

                int r0 = GetRank(t0);
                int r1 = GetRank(t1);
                int r2 = GetRank(t2);

                if (IsMixed(s0, s1, s2)) // 三色
                {
                    // 三色三节高
                    if (IsShifted1Unordered(r1, r0, r2))
                    {
                        return FanType.MixedShiftedPungs;
                    }
                    // 三同刻
                    if (r0 == r1 && r1 == r2)
                    {
                        return FanType.TriplePung;
                    }
                }
                else
                {
                    // 一色三节高
                    if (t0 + 1 == t1 && t1 + 1 == t2)
                    {
                        return FanType.PureShiftedPungs;
                    }
                }
            }
            else
            {
                // 三风刻
                if ((t0 == 0x41 && t1 == 0x42 && t2 == 0x43) ||
                    (t0 == 0x41 && t1 == 0x42 && t2 == 0x44) ||
                    (t0 == 0x41 && t1 == 0x43 && t2 == 0x44) ||
                    (t0 == 0x42 && t1 == 0x43 && t2 == 0x44))
                {
                    return FanType.BigThreeWinds;
                }
                // 大三元
                if (t0 == 0x45 && t1 == 0x46 && t2 == 0x47)
                {
                    return FanType.BigThreeDragons;
                }
            }

            // 以上都没有
            return FanType.None;
        }

        // 2组刻子的番
        private static FanType Get2PungsFanUnordered(int t0, int t1)
        {
            // 按出现频率顺序
            if (IsNumberedSuitQuick(t0) && IsNumberedSuitQuick(t1)) // 数牌
            {
                // 双同刻
                if (IsRankEqualQuick(t0, t1))
                {
                    return FanType.DoublePung;
                }
            }
            else
            {
                // 双箭刻
                if (IsDragons(t0) && IsDragons(t1))
                {
                    return FanType.TwoDragonsPungs;
                }
            }
            // 以上都没有
            return FanType.None;
        }

        // 1组刻子的番
        private static FanType Get1PungFan(int midTile)
        {
            // 箭刻
            if (IsDragons(midTile))
            {
                return FanType.DragonPung;
            }
            // 幺九刻
            if (IsTerminal(midTile) || IsWinds(midTile))
            {
                return FanType.PungOfTerminalsOrHonors;
            }
            // 以上都没有
            return FanType.None;
        }

        // 存在3组顺子的番种时，余下的第4组顺子最多算1番
        private static FanType Get1ChowExtraFan(int tile0, int tile1, int tile2, int tileExtra)
        {
            FanType fan0 = Get2ChowsFanUnordered(tile0, tileExtra);
            FanType fan1 = Get2ChowsFanUnordered(tile1, tileExtra);
            FanType fan2 = Get2ChowsFanUnordered(tile2, tileExtra);

            // 按以下顺序返回
            // 一般高
            if (fan0 == FanType.PureDoubleChow || fan1 == FanType.PureDoubleChow || fan2 == FanType.PureDoubleChow)
            {
                return FanType.PureDoubleChow;
            }
            // 喜相逢
            if (fan0 == FanType.MixedDoubleChow || fan1 == FanType.MixedDoubleChow || fan2 == FanType.MixedDoubleChow)
            {
                return FanType.MixedDoubleChow;
            }
            // 连六
            if (fan0 == FanType.ShortStraight || fan1 == FanType.ShortStraight || fan2 == FanType.ShortStraight)
            {
                return FanType.ShortStraight;
            }
            // 老少副
            if (fan0 == FanType.TwoTerminalChows || fan1 == FanType.TwoTerminalChows || fan2 == FanType.TwoTerminalChows)
            {
                return FanType.TwoTerminalChows;
            }

            return FanType.None;
        }

        // 套算一次原则和不得相同原则处理
        private static void ExclusionaryRule(FanType[] allFans, long fanCnt, long maxCnt, int[] fanTable)
        {
            // 统计有多少番
            int[] table = new int[4] { 0, 0, 0, 0 };
            long cnt = 0;
            for (long i = 0; i < fanCnt; i++)
            {
                if (allFans[i] != FanType.None)
                {
                    cnt++;
                    if (allFans[i] >= FanType.PureDoubleChow && allFans[i] <= FanType.TwoTerminalChows)
                    {
                        table[(int)allFans[i] - (int)FanType.PureDoubleChow]++;
                    }
                }
            }

            // 当超过时，从重复的开始削减
            int limitCnt = 1;
            // 第一轮先削减剩下1，第二轮削减剩下0
            while (cnt > maxCnt && limitCnt >= 0)
            {
                int idx = 3; // 从老少副开始削减
                while (cnt > maxCnt && idx >= 0)
                {
                    while (table[idx] > limitCnt && cnt > maxCnt)
                    {
                        table[idx]--;
                        cnt--;
                    }
                    idx--;
                }
                limitCnt--;
            }

            fanTable[(int)FanType.PureDoubleChow] = table[0];
            fanTable[(int)FanType.MixedDoubleChow] = table[1];
            fanTable[(int)FanType.ShortStraight] = table[2];
            fanTable[(int)FanType.TwoTerminalChows] = table[3];
        }

        // 4组顺子算番
        private static void Calculate4Chows(int[] midTiles, int[] fanTable)
        {
            FanType fan;
            // 存在4组顺子的番种时，不再检测其他的了
            if ((fan = Get4ChowsFan(midTiles[0], midTiles[1], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                return;
            }

            // 3组顺子判断
            // 012构成3组顺子的番种
            if ((fan = Get3ChowsFan(midTiles[0], midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                // 计算与第4组顺子构成的番
                if ((fan = Get1ChowExtraFan(midTiles[0], midTiles[1], midTiles[2], midTiles[3])) != FanType.None)
                {
                    fanTable[(int)fan] = 1;
                }
                return;
            }
            // 013构成3组顺子的番种
            else if ((fan = Get3ChowsFan(midTiles[0], midTiles[1], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                // 计算与第4组顺子构成的番
                if ((fan = Get1ChowExtraFan(midTiles[0], midTiles[1], midTiles[3], midTiles[2])) != FanType.None)
                {
                    fanTable[(int)fan] = 1;
                }
                return;
            }
            // 023构成3组顺子的番种
            else if ((fan = Get3ChowsFan(midTiles[0], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                // 计算与第4组顺子构成的番
                if ((fan = Get1ChowExtraFan(midTiles[0], midTiles[2], midTiles[3], midTiles[1])) != FanType.None)
                {
                    fanTable[(int)fan] = 1;
                }
                return;
            }
            // 123构成3组顺子的番种
            else if ((fan = Get3ChowsFan(midTiles[1], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                // 计算与第4组顺子构成的番
                if ((fan = Get1ChowExtraFan(midTiles[1], midTiles[2], midTiles[3], midTiles[0])) != FanType.None)
                {
                    fanTable[(int)fan] = 1;
                }
                return;
            }

            // 不存在3组顺子的番种时，4组顺子最多3番
            FanType[] allFans = new FanType[6]
            {
                Get2ChowsFanUnordered(midTiles[0], midTiles[1]),
                Get2ChowsFanUnordered(midTiles[0], midTiles[2]),
                Get2ChowsFanUnordered(midTiles[0], midTiles[3]),
                Get2ChowsFanUnordered(midTiles[1], midTiles[2]),
                Get2ChowsFanUnordered(midTiles[1], midTiles[3]),
                Get2ChowsFanUnordered(midTiles[2], midTiles[3])
            };

            int maxCnt = 3;

            // 0与其他3组顺子无任何关系
            if (allFans[0] == FanType.None && allFans[1] == FanType.None && allFans[2] == FanType.None)
            {
                maxCnt--;
            }

            // 1与其他3组顺子无任何关系
            if (allFans[0] == FanType.None && allFans[3] == FanType.None && allFans[4] == FanType.None)
            {
                maxCnt--;
            }

            // 2与其他3组顺子无任何关系
            if (allFans[1] == FanType.None && allFans[3] == FanType.None && allFans[5] == FanType.None)
            {
                maxCnt--;
            }

            // 3与其他3组顺子无任何关系
            if (allFans[2] == FanType.None && allFans[4] == FanType.None && allFans[5] == FanType.None)
            {
                maxCnt--;
            }

            if (maxCnt > 0)
            {
                ExclusionaryRule(allFans, 6, maxCnt, fanTable);
            }
        }

        // 3组顺子算番
        private static void Calculate3Chows(int[] midTiles, int[] fanTable)
        {
            FanType fan;

            // 存在3组顺子的番种时，不再检测其他的
            if ((fan = Get3ChowsFan(midTiles[0], midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                return;
            }

            // 不存在上述番种时，3组顺子最多2番
            FanType[] allFans = new FanType[3]
            {
                Get2ChowsFanUnordered(midTiles[0], midTiles[1]),
                Get2ChowsFanUnordered(midTiles[0], midTiles[2]),
                Get2ChowsFanUnordered(midTiles[1], midTiles[2])
            };
            ExclusionaryRule(allFans, 3, 2, fanTable);
        }

        // 2组顺子算番
        private static void Calculate2ChowsUnordered(int[] midTiles, int[] fanTable)
        {
            FanType fan;
            if ((fan = Get2ChowsFanUnordered(midTiles[0], midTiles[1])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
        }

        // 刻子（杠）算番
        private static void CalculateKongs(Pack[] pungPacks, int pungCnt, int[] fanTable)
        {
            // 统计明杠 暗杠 明刻 暗刻
            int meldedKongCnt = 0;
            int concealedKongCnt = 0;
            int concealedPungCnt = 0;
            for (int i = 0; i < pungCnt; i++)
            {
                if (pungPacks[i].IsMelded)
                {
                    if (pungPacks[i].Type == PackType.Kong)
                    {
                        meldedKongCnt++;
                    }
                }
                else
                {
                    if (pungPacks[i].Type == PackType.Kong)
                    {
                        concealedKongCnt++;
                    }
                    else
                    {
                        concealedPungCnt++;
                    }
                }
            }

            // 计算杠相关番
            int kongCnt = meldedKongCnt + concealedKongCnt;
            switch (kongCnt)
            {
                case 0: // 0个杠
                    switch (concealedPungCnt) // 暗刻的个数
                    {
                        case 2: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                        case 3: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                        case 4: fanTable[(int)FanType.FourConcealedPungs] = 1; break;
                    }
                    break;
                case 1: // 1个杠
                    if (meldedKongCnt == 1) // 明杠
                    {
                        fanTable[(int)FanType.MeldedKong] = 1;
                        switch (concealedPungCnt) // 暗刻的个数
                        {
                            case 2: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                            case 3: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                        }
                    }
                    else // 暗杠
                    {
                        fanTable[(int)FanType.ConcealedKong] = 1;
                        switch (concealedPungCnt) // 暗刻的个数
                        {
                            case 1: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                            case 2: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                            case 3: fanTable[(int)FanType.FourConcealedPungs] = 1; break;
                        }
                    }
                    break;
                case 2: // 2个杠
                    switch (concealedKongCnt)
                    {
                        case 0: // 双明杠
                            fanTable[(int)FanType.TwoMeldedKongs] = 1;
                            switch (concealedPungCnt) // 暗刻的个数
                            {
                                case 2: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                            }
                            break;
                        case 1: // 明暗杠
                            fanTable[(int)FanType.ConcealedKongAndMeldedKong] = 1;
                            switch (concealedPungCnt) // 暗刻的个数
                            {
                                case 1: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                                case 2: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                            }
                            break;
                        case 2: // 双暗杠
                            fanTable[(int)FanType.TwoConcealedKongs] = 1;
                            switch (concealedPungCnt) // 暗刻的个数
                            {
                                case 1: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                                case 2: fanTable[(int)FanType.FourConcealedPungs] = 1; break;
                            }
                            break;
                    }
                    break;
                case 3: // 3个杠
                    fanTable[(int)FanType.ThreeKongs] = 1;
                    switch (concealedKongCnt) // 暗刻的个数
                    {
                        case 0: // 3明杠
                            break;
                        case 1: // 1暗杠2明杠
                            if (concealedPungCnt > 0)
                            {
                                fanTable[(int)FanType.TwoConcealedPungs] = 1;
                            }
                            break;
                        case 2: // 2暗杠1明杠
                            if (concealedPungCnt == 0)
                            {
                                fanTable[(int)FanType.TwoConcealedPungs] = 1;
                            }
                            else
                            {
                                fanTable[(int)FanType.ThreeConcealedPungs] = 1;
                            }
                            break;
                        case 3: // 3暗杠
                            if (concealedPungCnt == 0)
                            {
                                fanTable[(int)FanType.ThreeConcealedPungs] = 1;
                            }
                            else
                            {
                                fanTable[(int)FanType.FourConcealedPungs] = 1;
                            }
                            break;
                    }
                    break;
                case 4: // 4个杠
                    fanTable[(int)FanType.FourKongs] = 1;
                    switch (concealedKongCnt)
                    {
                        case 2: fanTable[(int)FanType.TwoConcealedPungs] = 1; break;
                        case 3: fanTable[(int)FanType.ThreeConcealedPungs] = 1; break;
                        case 4: fanTable[(int)FanType.FourConcealedPungs] = 1; break;
                    }
                    break;
            }

            // 四杠和四暗刻不计碰碰和，其他先加上碰碰和的番
            if (pungCnt == 4)
            {
                if (fanTable[(int)FanType.FourKongs] == 0 && fanTable[(int)FanType.FourConcealedPungs] == 0)
                {
                    fanTable[(int)FanType.AllPungs] = 1;
                }
            }

            // 逐组刻子的番（箭刻、幺九刻）
            for (int i = 0; i < pungCnt; i++)
            {
                FanType fan = Get1PungFan(pungPacks[i].Tile);
                if (fan != FanType.None)
                {
                    fanTable[(int)fan]++;
                }
            }
        }

        // 4组刻子算番
        private static void Calculate4Pungs(int[] midTiles, int[] fanTable)
        {
            FanType fan;
            // 存在4组刻子的番种时，不再检测其他的了
            if ((fan = Get4PungsFan(midTiles[0], midTiles[1], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                return;
            }

            // 3组刻子判断
            bool threePungsHasFan = false;
            int freePackIdx = -1; // 未使用的1组刻子
            // 012构成3组刻子的番种
            if ((fan = Get3PungsFan(midTiles[0], midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                freePackIdx = 3;
                threePungsHasFan = true;
            }
            // 013构成3组刻子的番种
            else if ((fan = Get3PungsFan(midTiles[0], midTiles[1], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                freePackIdx = 2;
                threePungsHasFan = true;
            }
            // 023构成3组刻子的番种
            else if ((fan = Get3PungsFan(midTiles[0], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                freePackIdx = 1;
                threePungsHasFan = true;
            }
            // 123构成3组刻子的番种
            else if ((fan = Get3PungsFan(midTiles[1], midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                freePackIdx = 0;
                threePungsHasFan = true;
            }

            // 存在3组刻子的番种时，余下的第4组刻子只能组合一次
            if (threePungsHasFan)
            {
                for (int i = 0; i < 4; i++)
                {
                    if (i == freePackIdx)
                        continue;
                    // 依次与未使用的这组刻子测试番种
                    if ((fan = Get2PungsFanUnordered(midTiles[i], midTiles[freePackIdx])) != FanType.None)
                    {
                        fanTable[(int)fan]++;
                        break;
                    }
                }
                return;
            }

            // 不存在3组刻子的番种时，两两计算番种
            if ((fan = Get2PungsFanUnordered(midTiles[0], midTiles[1])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[0], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[0], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[1], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[2], midTiles[3])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
        }

        // 3组刻子算番
        private static void Calculate3Pungs(int[] midTiles, int[] fanTable)
        {
            FanType fan;

            // 存在3组刻子的番种（三节高 三同刻 三风刻 大三元）时，不再检测其他的
            if ((fan = Get3PungsFan(midTiles[0], midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan] = 1;
                return;
            }

            // 不存在3组刻子的番种时，两两计算番种
            if ((fan = Get2PungsFanUnordered(midTiles[0], midTiles[1])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[0], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
            if ((fan = Get2PungsFanUnordered(midTiles[1], midTiles[2])) != FanType.None)
            {
                fanTable[(int)fan]++;
            }
        }

        // 2组刻子算番
        private static void Calculate2PungsUnordered(int[] midTiles, int[] fanTable)
        {
            FanType fan = Get2PungsFanUnordered(midTiles[0], midTiles[1]);
            if (fan != FanType.None)
            {
                fanTable[(int)fan]++;
            }
        }

        // 九莲宝灯
        private static bool IsNineGates(int[] tiles)
        {
            // 对立牌的种类进行打表
            int[] cntTable = new int[136];
            MapTiles(tiles, 13, cntTable);

            // 1、9各三枚，2~8各一枚
            return (cntTable[0x11] == 3 && cntTable[0x19] == 3 &&
                Enumerable.Range(0x12, 7).All(r => cntTable[r] == 1)) ||
                   (cntTable[0x21] == 3 && cntTable[0x29] == 3 &&
                Enumerable.Range(0x22, 7).All(r => cntTable[r] == 1)) ||
                   (cntTable[0x31] == 3 && cntTable[0x39] == 3 &&
                Enumerable.Range(0x32, 7).All(r => cntTable[r] == 1));
        }

        // 一色双龙会
        private static bool IsPureTerminalChows(Pack[] chowPacks, Pack pairPack)
        {
            if (GetRank(pairPack.Tile) != 5) // 5作雀头
            {
                return false;
            }

            int oneTwoThreeCnt = 0, sevenEightNineCnt = 0;
            int pairSuit = GetSuit(pairPack.Tile);
            for (int i = 0; i < 4; i++)
            {
                int suit = GetSuit(chowPacks[i].Tile);
                if (suit != pairSuit) // 花色与雀头相同
                {
                    return false;
                }
                int rank = GetRank(chowPacks[i].Tile);
                switch (rank)
                {
                    case 2: oneTwoThreeCnt++; break;
                    case 8: sevenEightNineCnt++; break;
                    default: return false;
                }
            }
            return (oneTwoThreeCnt == 2 && sevenEightNineCnt == 2); // 123和789各2组
        }

        // 三色双龙会
        private static bool IsThreeSuitedTerminalChows(Pack[] chowPacks, Pack pairPack)
        {
            if (GetRank(pairPack.Tile) != 5) // 5作雀头
            {
                return false;
            }

            int[] oneTwoThreeSuitTable = new int[4] { 0, 0, 0, 0 };
            int[] sevenEightNineSuitTable = new int[4] { 0, 0, 0, 0 };
            int pairSuit = GetSuit(pairPack.Tile);
            for (int i = 0; i < 4; i++)
            {
                int suit = GetSuit(chowPacks[i].Tile);
                if (suit == pairSuit) // 花色与雀头不相同
                {
                    return false;
                }
                int rank = GetRank(chowPacks[i].Tile);
                switch (rank)
                {
                    case 2: oneTwoThreeSuitTable[suit]++; break;
                    case 8: sevenEightNineSuitTable[suit]++; break;
                    default: return false;
                }
            }

            // 与雀头花色不同的两色123和789各一组
            switch (pairSuit)
            {
                case 1: return (oneTwoThreeSuitTable[2] > 0 && oneTwoThreeSuitTable[3] > 0 && sevenEightNineSuitTable[2] > 0 && sevenEightNineSuitTable[3] > 0);
                case 2: return (oneTwoThreeSuitTable[1] > 0 && oneTwoThreeSuitTable[3] > 0 && sevenEightNineSuitTable[1] > 0 && sevenEightNineSuitTable[3] > 0);
                case 3: return (oneTwoThreeSuitTable[1] > 0 && oneTwoThreeSuitTable[2] > 0 && sevenEightNineSuitTable[1] > 0 && sevenEightNineSuitTable[2] > 0);
                default: return false;
            }
        }

        // 根据和牌方式调整——涉及番种：不求人、全求人
        private static void AdjustBySelfDrawn(Pack[] packs, int fixedCnt, bool selfDrawn, int[] fanTable)
        {
            int meldedCnt = 0;
            for (int i = 0; i < fixedCnt; i++)
            {
                if (packs[i].IsMelded)
                {
                    meldedCnt++;
                }
            }

            switch (meldedCnt)
            {
                case 0: // 0组明的，自摸为不求人，点和为门前清
                    fanTable[(int)(selfDrawn ? FanType.FullyConcealedHand : FanType.ConcealedHand)] = 1;
                    break;
                case 4:
                    // 4组明的，自摸还是自摸，点和为全求人
                    fanTable[(int)(selfDrawn ? FanType.SelfDrawn : FanType.MeldedHand)] = 1;
                    break;
                default:
                    if (selfDrawn)
                    {
                        fanTable[(int)FanType.SelfDrawn] = 1;
                    }
                    break;
            }
        }

        // 根据雀头调整——涉及番种：平和、小三元、小四喜
        private static void AdjustByPairTile(int pairTile, int chowCnt, int[] fanTable)
        {
            if (chowCnt == 4) // 4组都是顺子
            {
                if (IsNumberedSuitQuick(pairTile)) // 数牌雀头
                {
                    fanTable[(int)FanType.AllChows] = 1; // 平和
                }
                return;
            }

            // 在双箭刻的基础上，如果雀头是箭牌，则修正为小三元
            if (fanTable[(int)FanType.TwoDragonsPungs] > 0)
            {
                if (IsDragons(pairTile))
                {
                    fanTable[(int)FanType.LittleThreeDragons] = 1;
                    fanTable[(int)FanType.TwoDragonsPungs] = 0;
                }
                return;
            }
            // 在三风刻的基础上，如果雀头是风牌，则修正为小四喜
            if (fanTable[(int)FanType.BigThreeWinds] > 0)
            {
                if (IsWinds(pairTile))
                {
                    fanTable[(int)FanType.LittleFourWinds] = 1;
                    fanTable[(int)FanType.BigThreeWinds] = 0;
                }
                return;
            }
        }

        // 根据花色调整——涉及番种：无字、缺一门、混一色、清一色、五门齐
        private static void AdjustBySuits(int[] tiles, int tileCnt, int[] fanTable)
        {
            // 打表标记有哪些花色，用bit操作
            int suitFlag = 0;
            for (int i = 0; i < tileCnt; i++)
            {
                suitFlag |= (1 << GetSuit(tiles[i]));
            }

            // 1111 0001
            if ((suitFlag & 0xF1) == 0)
            {
                fanTable[(int)FanType.NoHonors] = 1; // 无字
            }

            // 1110 0011
            if ((suitFlag & 0xE3) == 0)
            {
                fanTable[(int)FanType.OneVoidedSuit]++; // 缺一门（万）
            }
            // 1110 0101
            if ((suitFlag & 0xE5) == 0)
            {
                fanTable[(int)FanType.OneVoidedSuit]++; // 缺一门（条）
            }
            // 1110 1001
            if ((suitFlag & 0xE9) == 0)
            {
                fanTable[(int)FanType.OneVoidedSuit]++; // 缺一门（饼）
            }

            // 当缺2门时，根据有字和无字，修正为混一色和清一色
            if (fanTable[(int)FanType.OneVoidedSuit] == 2)
            {
                fanTable[(int)FanType.OneVoidedSuit] = 0;
                fanTable[(suitFlag & 0xF1) != 0 ? (int)FanType.HalfFlush : (int)FanType.FullFlush] = 1;
            }

            // 0001 1110
            if (suitFlag == 0x1E) // 三门数牌和字牌都有
            {
                if (tiles.Any(t => IsWinds(t)) && tiles.Any(t => IsDragons(t)))
                {
                    fanTable[(int)FanType.AllTypes] = 1; // 五门齐
                }
            }
        }

        // 根据数牌的范围调整——涉及番种：大于五、小于五、全大、全中、全小
        private static void AdjustByRankRange(int[] tiles, int tileCnt, int[] fanTable)
        {
            // 打表标记有哪些数
            int rankFlag = 0;
            for (int i = 0; i < tileCnt; i++)
            {
                if (!IsNumberedSuitQuick(tiles[i]))
                {
                    return;
                }
                rankFlag |= (1 << GetRank(tiles[i]));
            }

            // 1111 1111 1110 0001
            // 检测是否只包含1234
            if ((rankFlag & 0xFFE1) == 0)
            {
                // 包含4为小于五，否则为全小
                fanTable[((rankFlag & 0x0010) != 0) ? (int)FanType.LowerFour : (int)FanType.LowerTiles] = 1;
                return;
            }
            // 1111 1100 0011 1111
            // 检测是否只包含6789
            if ((rankFlag & 0xFC3F) == 0)
            {
                // 包含6为大于五，否则为全大
                fanTable[((rankFlag & 0x0040) != 0) ? (int)FanType.UpperFour : (int)FanType.UpperTiles] = 1;
                return;
            }
            // 1111 1111 1000 1111
            // 检测是否只包含456
            if ((rankFlag & 0xFF8F) == 0)
            {
                // 全中
                fanTable[(int)FanType.MiddleTiles] = 1;
            }
        }

        // 根据牌组特征调整——涉及番种：全带幺、全带五、全双刻
        private static void AdjustByPacksTraits(Pack[] packs, int[] fanTable)
        {
            // 统计包含数牌19、字牌、5、双数牌的组数
            int terminalPack = 0;
            int honorPack = 0;
            int fivePack = 0;
            int evenPack = 0;
            for (int i = 0; i < 5; i++)
            {
                int tile = packs[i].Tile;
                if (IsNumberedSuitQuick(tile)) // 数牌
                {
                    int rank = GetRank(tile);
                    if (packs[i].Type == PackType.Chow) // 顺子
                    {
                        switch (rank)
                        {
                            case 2: case 8: terminalPack++; break; // 数牌19
                            case 4: case 5: case 6: fivePack++; break; // 带五
                        }
                    }
                    else // 刻子或雀头
                    {
                        switch (rank)
                        {
                            case 1: case 9: terminalPack++; break; // 数牌19
                            case 5: fivePack++; break; // 带五
                            case 2: case 4: case 6: case 8: evenPack++; break; // 双刻
                        }
                    }
                }
                else
                {
                    honorPack++; // 字牌
                }
            }

            // 5组牌都包含数牌19和字牌，先暂时计为全带幺
            if (terminalPack + honorPack == 5)
            {
                fanTable[(int)FanType.OutsideHand] = 1;
                return;
            }
            // 全带五
            if (fivePack == 5)
            {
                fanTable[(int)FanType.AllFive] = 1;
                return;
            }
            // 全双刻
            if (evenPack == 5)
            {
                fanTable[(int)FanType.AllEvenPungs] = 1;
            }
        }

        // 根据牌特性调整——涉及番种：断幺、推不倒、绿一色、字一色、清幺九、混幺九
        private static void AdjustByTilesTraits(int[] tiles, int tileCnt, int[] fanTable)
        {
            // 断幺
            if (tiles.Take(tileCnt).All(t => !IsTerminalOrHonor(t)))
            {
                fanTable[(int)FanType.AllSimples] = 1;
            }

            // 推不倒
            if (tiles.Take(tileCnt).All(t => IsReversible(t)))
            {
                fanTable[(int)FanType.ReversibleTiles] = 1;
            }

            // 绿一色
            if (tiles.Take(tileCnt).All(t => IsGreen(t)))
            {
                fanTable[(int)FanType.AllGreen] = 1;
            }

            // 如果断幺了就没必要检测字一色、清幺九、混幺九了
            if (fanTable[(int)FanType.AllSimples] > 0)
            {
                return;
            }

            // 字一色
            if (tiles.Take(tileCnt).All(t => IsHonor(t)))
            {
                fanTable[(int)FanType.AllHonors] = 1;
                return;
            }

            // 清幺九
            if (tiles.Take(tileCnt).All(t => IsTerminal(t)))
            {
                fanTable[(int)FanType.AllTerminals] = 1;
                return;
            }

            // 混幺九
            if (tiles.Take(tileCnt).All(t => IsTerminalOrHonor(t)))
            {
                fanTable[(int)FanType.AllTerminalsAndHonors] = 1;
            }
        }

        // 四归一调整
        private static void AdjustByTilesHog(int[] tiles, int tileCnt, int[] fanTable)
        {
            int kongCnt = tileCnt - 14; // 标准和牌14张，多出几张就说明有几个杠
            int[] cntTable = new int[136];
            MapTiles(tiles, tileCnt, cntTable);
            // 有多少种已经用去4张的牌减去杠的数量，即为四归一的数量
            int fourCnt = cntTable.Count(c => c == 4);
            fanTable[(int)FanType.TileHog] = fourCnt - kongCnt;
        }

        // 根据听牌方式调整——涉及番种：边张、嵌张、单钓将
        private static void AdjustByWaitingForm(Pack[] concealedPacks, int packCnt, int[] standingTiles, int standingCnt, int winTile, int[] fanTable)
        {
            // 全求人和四杠不计单钓将，也不可能有边张、嵌张
            if (fanTable[(int)FanType.MeldedHand] > 0 || fanTable[(int)FanType.FourKongs] > 0)
            {
                return;
            }

            bool[] waitingTable = new bool[136];
            if (!IsBasicFormWait(standingTiles, standingCnt, waitingTable))
            {
                return;
            }

            if (packCnt == 5) // 门清状态
            {
                // 判断是否为七对听牌
                bool[] tempTable = new bool[136];
                if (IsSevenPairsWait(standingTiles, standingCnt, tempTable))
                {
                    // 合并听牌标记表
                    for (int i = 0; i < 136; i++)
                    {
                        waitingTable[i] = waitingTable[i] || tempTable[i];
                    }
                }
            }

            // 统计听牌张数，听牌数大于1张，不计边张、嵌张、单钓将
            if (waitingTable.Count(w => w) != 1)
            {
                return;
            }

            // 听1张的情况，看和牌张处于什么位置
            // 边张0x01 嵌张0x02 单钓将0x04
            int posFlag = 0;

            for (int i = 5 - packCnt; i < 5; i++)
            {
                switch (concealedPacks[i].Type)
                {
                    case PackType.Chow:
                        int midTile = concealedPacks[i].Tile;
                        if (midTile == winTile)
                        {
                            posFlag |= 0x02; // 嵌张
                        }
                        else if (midTile + 1 == winTile || midTile - 1 == winTile)
                        {
                            posFlag |= 0x01; // 边张
                        }
                        break;
                    case PackType.Pair:
                        int midTile1 = concealedPacks[i].Tile;
                        if (midTile1 == winTile)
                        {
                            posFlag |= 0x04; // 单钓将
                        }
                        break;
                }
            }

            // 当多种可能存在时，只能计其中一种
            if ((posFlag & 0x01) != 0)
            {
                fanTable[(int)FanType.EdgeWait] = 1;
            }
            else if ((posFlag & 0x02) != 0)
            {
                fanTable[(int)FanType.ClosedWait] = 1;
            }
            else if ((posFlag & 0x04) != 0)
            {
                fanTable[(int)FanType.SingleWait] = 1;
            }
        }

        // 判断基本听牌形式
        private static bool IsBasicFormWait(int[] standingTiles, int standingCnt, bool[] waitingTable)
        {
            Array.Clear(waitingTable, 0, waitingTable.Length);

            DivisionResult divisionResult = new DivisionResult();
            Pack[] packs = new Pack[5];
            int packCount = (14 - standingCnt) / 3;
            bool r = false;
            foreach (int card in AllTiles)
            {
                int[] tiles = new int[14];
                Array.Copy(standingTiles, tiles, standingCnt);
                tiles[standingCnt] = card;
                if (DivideWinHand(tiles, packs, packCount, divisionResult))
                {
                    waitingTable[card] = true;
                    r = true;
                }
            }

            return r;
        }

        // 判断七对听牌
        private static bool IsSevenPairsWait(int[] standingTiles, int standingCnt, bool[] waitingTable)
        {
            Array.Clear(waitingTable, 0, waitingTable.Length);

            if (standingCnt < 13)
                return false;

            bool r = false;
            foreach (int card in AllTiles)
            {
                int[] tiles = new int[14];
                Array.Copy(standingTiles, tiles, standingCnt);
                tiles[standingCnt] = card;
                Array.Sort(tiles);
                if (tiles[0] == tiles[1] &&
                    tiles[2] == tiles[3] &&
                    tiles[4] == tiles[5] &&
                    tiles[6] == tiles[7] &&
                    tiles[8] == tiles[9] &&
                    tiles[10] == tiles[11] &&
                    tiles[12] == tiles[13])
                {
                    waitingTable[card] = true;
                    r = true;
                }
            }

            return r;
        }

        // 统一调整一些不计的
        private static void AdjustFanTable(int[] fanTable)
        {
            // 大四喜不计三风刻、碰碰和、圈风刻、门风刻、幺九刻
            if (fanTable[(int)FanType.BigFourWinds] > 0)
            {
                fanTable[(int)FanType.BigThreeWinds] = 0;
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = 0;
            }
            // 大三元不计双箭刻、箭刻（严格98规则不计缺一门）
            if (fanTable[(int)FanType.BigThreeDragons] > 0)
            {
                fanTable[(int)FanType.TwoDragonsPungs] = 0;
                fanTable[(int)FanType.DragonPung] = 0;
            }
            // 绿一色不计混一色、缺一门
            if (fanTable[(int)FanType.AllGreen] > 0)
            {
                fanTable[(int)FanType.HalfFlush] = 0;
                fanTable[(int)FanType.OneVoidedSuit] = 0;
            }
            // 九莲宝灯不计清一色、门前清、缺一门、无字，减计1个幺九刻，把不求人修正为自摸
            if (fanTable[(int)FanType.NineGates] > 0)
            {
                fanTable[(int)FanType.FullFlush] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = Math.Max(0, fanTable[(int)FanType.PungOfTerminalsOrHonors] - 1);
                fanTable[(int)FanType.OneVoidedSuit] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
                if (fanTable[(int)FanType.FullyConcealedHand] > 0)
                {
                    fanTable[(int)FanType.FullyConcealedHand] = 0;
                    fanTable[(int)FanType.SelfDrawn] = 1;
                }
            }
            // 四杠不计单钓将
            if (fanTable[(int)FanType.FourKongs] > 0)
            {
                fanTable[(int)FanType.SingleWait] = 0;
            }
            // 连七对不计七对、清一色、门前清、缺一门、无字
            if (fanTable[(int)FanType.SevenShiftedPairs] > 0)
            {
                fanTable[(int)FanType.SevenPairs] = 0;
                fanTable[(int)FanType.FullFlush] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
                fanTable[(int)FanType.OneVoidedSuit] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 十三幺不计五门齐、门前清、单钓将
            if (fanTable[(int)FanType.ThirteenOrphans] > 0)
            {
                fanTable[(int)FanType.AllTypes] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
                fanTable[(int)FanType.SingleWait] = 0;
            }

            // 清幺九不计混幺九、碰碰胡、全带幺、幺九刻、无字（严格98规则不计双同刻、不计三同刻）
            if (fanTable[(int)FanType.AllTerminals] > 0)
            {
                fanTable[(int)FanType.AllTerminalsAndHonors] = 0;
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.OutsideHand] = 0;
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
                fanTable[(int)FanType.DoublePung] = 0; // 通行计法不计双同刻
            }

            // 小四喜不计三风刻
            if (fanTable[(int)FanType.LittleFourWinds] > 0)
            {
                fanTable[(int)FanType.BigThreeWinds] = 0;
                // 小四喜的第四组牌如果是19的刻子，则是混幺九；如果是箭刻则是字一色；这两种都是不计幺九刻的
                // 如果是顺子或者2-8的刻子，则不存在多余的幺九刻
                // 所以这里将幺九刻置为0
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = 0;
            }

            // 小三元不计双箭刻、箭刻（严格98规则不计缺一门）
            if (fanTable[(int)FanType.LittleThreeDragons] > 0)
            {
                fanTable[(int)FanType.TwoDragonsPungs] = 0;
                fanTable[(int)FanType.DragonPung] = 0;
            }

            // 字一色不计混幺九、碰碰胡、全带幺、幺九刻、缺一门
            if (fanTable[(int)FanType.AllHonors] > 0)
            {
                fanTable[(int)FanType.AllTerminalsAndHonors] = 0;
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.OutsideHand] = 0;
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = 0;
                fanTable[(int)FanType.OneVoidedSuit] = 0;
            }
            // 四暗刻不计碰碰和、门前清，把不求人修正为自摸
            if (fanTable[(int)FanType.FourConcealedPungs] > 0)
            {
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
                if (fanTable[(int)FanType.FullyConcealedHand] > 0)
                {
                    fanTable[(int)FanType.FullyConcealedHand] = 0;
                    fanTable[(int)FanType.SelfDrawn] = 1;
                }
            }
            // 一色双龙会不计七对、清一色、平和、一般高、老少副、缺一门、无字
            if (fanTable[(int)FanType.PureTerminalChows] > 0)
            {
                fanTable[(int)FanType.SevenPairs] = 0;
                fanTable[(int)FanType.FullFlush] = 0;
                fanTable[(int)FanType.AllChows] = 0;
                fanTable[(int)FanType.PureDoubleChow] = 0;
                fanTable[(int)FanType.TwoTerminalChows] = 0;
                fanTable[(int)FanType.OneVoidedSuit] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }

            // 一色四同顺不计一色三同顺、一般高、四归一（严格98规则不计缺一门）
            if (fanTable[(int)FanType.QuadrupleChow] > 0)
            {
                fanTable[(int)FanType.PureShiftedPungs] = 0;
                fanTable[(int)FanType.TileHog] = 0;
                fanTable[(int)FanType.PureDoubleChow] = 0;
            }
            // 一色四节高不计一色三节高、碰碰和（严格98规则不计缺一门）
            if (fanTable[(int)FanType.FourPureShiftedPungs] > 0)
            {
                fanTable[(int)FanType.PureTripleChow] = 0;
                fanTable[(int)FanType.AllPungs] = 0;
            }

            // 一色四步高不计一色三步高、老少副、连六（严格98规则不计缺一门）
            if (fanTable[(int)FanType.FourPureShiftedChows] > 0)
            {
                fanTable[(int)FanType.PureShiftedChows] = 0;
                fanTable[(int)FanType.TwoTerminalChows] = 0;
                fanTable[(int)FanType.ShortStraight] = 0;
            }

            // 混幺九不计碰碰和、全带幺、幺九刻
            if (fanTable[(int)FanType.AllTerminalsAndHonors] > 0)
            {
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.OutsideHand] = 0;
                fanTable[(int)FanType.PungOfTerminalsOrHonors] = 0;
            }

            // 七对不计门前清、单钓将
            if (fanTable[(int)FanType.SevenPairs] > 0)
            {
                fanTable[(int)FanType.ConcealedHand] = 0;
                fanTable[(int)FanType.SingleWait] = 0;
            }
            // 七星不靠不计五门齐、门前清
            if (fanTable[(int)FanType.GreaterHonorsAndKnittedTiles] > 0)
            {
                fanTable[(int)FanType.AllTypes] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
            }
            // 全双刻不计碰碰胡、断幺、无字
            if (fanTable[(int)FanType.AllEvenPungs] > 0)
            {
                fanTable[(int)FanType.AllPungs] = 0;
                fanTable[(int)FanType.AllSimples] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 清一色不计缺一门、无字
            if (fanTable[(int)FanType.FullFlush] > 0)
            {
                fanTable[(int)FanType.OneVoidedSuit] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 一色三同顺不计一色三节高、一般高
            if (fanTable[(int)FanType.PureTripleChow] > 0)
            {
                fanTable[(int)FanType.PureShiftedPungs] = 0;
                fanTable[(int)FanType.PureDoubleChow] = 0;
            }
            // 一色三节高不计一色三同顺
            if (fanTable[(int)FanType.PureShiftedPungs] > 0)
            {
                fanTable[(int)FanType.PureTripleChow] = 0;
            }
            // 全大不计大于五、无字
            if (fanTable[(int)FanType.UpperTiles] > 0)
            {
                fanTable[(int)FanType.UpperFour] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 全中不计断幺
            if (fanTable[(int)FanType.MiddleTiles] > 0)
            {
                fanTable[(int)FanType.AllSimples] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 全小不计小于五、无字
            if (fanTable[(int)FanType.LowerTiles] > 0)
            {
                fanTable[(int)FanType.LowerFour] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }

            // 三色双龙会不计平和、无字、喜相逢、老少副
            if (fanTable[(int)FanType.ThreeSuitedTerminalChows] > 0)
            {
                fanTable[(int)FanType.AllChows] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
                fanTable[(int)FanType.MixedDoubleChow] = 0;
                fanTable[(int)FanType.TwoTerminalChows] = 0;
            }
            // 全带五不计断幺、无字
            if (fanTable[(int)FanType.AllFive] > 0)
            {
                fanTable[(int)FanType.AllSimples] = 0;
                fanTable[(int)FanType.NoHonors] = 0;
            }

            // 七星不靠不计五门齐、门前清
            if (fanTable[(int)FanType.LesserHonorsAndKnittedTiles] > 0)
            {
                fanTable[(int)FanType.AllTypes] = 0;
                fanTable[(int)FanType.ConcealedHand] = 0;
            }
            // 大于五不计无字
            if (fanTable[(int)FanType.UpperFour] > 0)
            {
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 小于五不计无字
            if (fanTable[(int)FanType.LowerFour] > 0)
            {
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 三风刻内部不再计幺九刻（严格98规则不计缺一门）
            if (fanTable[(int)FanType.BigThreeWinds] > 0)
            {
                // 如果不是字一色或混幺九，则要减去3个幺九刻
                if (fanTable[(int)FanType.AllHonors] == 0 && fanTable[(int)FanType.AllTerminalsAndHonors] == 0)
                {
                    fanTable[(int)FanType.PungOfTerminalsOrHonors] = Math.Max(0, fanTable[(int)FanType.PungOfTerminalsOrHonors] - 3);
                }
            }

            // 推不倒不计缺一门
            if (fanTable[(int)FanType.ReversibleTiles] > 0)
            {
                fanTable[(int)FanType.OneVoidedSuit] = 0;
            }
            // 妙手回春不计自摸
            if (fanTable[(int)FanType.LastTileDraw] > 0)
            {
                fanTable[(int)FanType.SelfDrawn] = 0;
            }
            // 杠上开花不计自摸
            if (fanTable[(int)FanType.OutWithReplacementTile] > 0)
            {
                fanTable[(int)FanType.SelfDrawn] = 0;
            }
            // 抢杠和不计和绝张
            if (fanTable[(int)FanType.RobbingTheKong] > 0)
            {
                fanTable[(int)FanType.LastTile] = 0;
            }
            // 双暗杠不计暗杠
            if (fanTable[(int)FanType.TwoConcealedKongs] > 0)
            {
                fanTable[(int)FanType.ConcealedKong] = 0;
            }

            // 混一色不计缺一门
            if (fanTable[(int)FanType.HalfFlush] > 0)
            {
                fanTable[(int)FanType.OneVoidedSuit] = 0;
            }
            // 全求人不计单钓将
            if (fanTable[(int)FanType.MeldedHand] > 0)
            {
                fanTable[(int)FanType.SingleWait] = 0;
            }
            // 双箭刻不计箭刻
            if (fanTable[(int)FanType.TwoDragonsPungs] > 0)
            {
                fanTable[(int)FanType.DragonPung] = 0;
            }

            // 不求人不计自摸
            if (fanTable[(int)FanType.FullyConcealedHand] > 0)
            {
                fanTable[(int)FanType.SelfDrawn] = 0;
            }
            // 双明杠不计明杠
            if (fanTable[(int)FanType.TwoMeldedKongs] > 0)
            {
                fanTable[(int)FanType.MeldedKong] = 0;
            }

            // 平和不计无字
            if (fanTable[(int)FanType.AllChows] > 0)
            {
                fanTable[(int)FanType.NoHonors] = 0;
            }
            // 断幺不计无字
            if (fanTable[(int)FanType.AllSimples] > 0)
            {
                fanTable[(int)FanType.NoHonors] = 0;
            }
        }

        // 调整圈风刻、门风刻
        private static void AdjustByWinds(int tile, Wind prevalentWind, Wind seatWind, int[] fanTable)
        {
            // 三风刻、混幺九、字一色、小四喜，这些番种已经扣除过幺九刻了
            bool isDeducted = (fanTable[(int)FanType.BigThreeWinds] > 0 ||
                              fanTable[(int)FanType.AllTerminalsAndHonors] > 0 ||
                              fanTable[(int)FanType.AllHonors] > 0 ||
                              fanTable[(int)FanType.LittleFourWinds] > 0);

            int delta = tile - 0x41;
            if (delta == (int)prevalentWind)
            {
                fanTable[(int)FanType.PrevalentWind] = 1;
                if (!isDeducted)
                {
                    fanTable[(int)FanType.PungOfTerminalsOrHonors] = Math.Max(0, fanTable[(int)FanType.PungOfTerminalsOrHonors] - 1);
                }
            }
            if (delta == (int)seatWind)
            {
                fanTable[(int)FanType.SeatWind] = 1;
                // 当圈风门风相同时，已经扣除过幺九刻了
                if (seatWind != prevalentWind && !isDeducted)
                {
                    fanTable[(int)FanType.PungOfTerminalsOrHonors] = Math.Max(0, fanTable[(int)FanType.PungOfTerminalsOrHonors] - 1);
                }
            }
        }

        // 根据和牌标记调整——涉及番种：和绝张、妙手回春、海底捞月、自摸
        private static void AdjustByWinFlag(WinFlag winFlag, int[] fanTable)
        {
            if ((winFlag & WinFlag._4thTile) != 0)
            {
                fanTable[(int)FanType.LastTile] = 1;
            }
            if ((winFlag & WinFlag.WallLast) != 0)
            {
                fanTable[(int)((winFlag & WinFlag.SelfDrawn) != 0 ? FanType.LastTileDraw : FanType.LastTileClaim)] = 1;
            }
            if ((winFlag & WinFlag.AboutKong) != 0)
            {
                fanTable[(int)((winFlag & WinFlag.SelfDrawn) != 0 ? FanType.OutWithReplacementTile : FanType.RobbingTheKong)] = 1;
            }
            if ((winFlag & WinFlag.SelfDrawn) != 0)
            {
                fanTable[(int)FanType.SelfDrawn] = 1;
            }
        }

        // 基本和型算番
        private static void CalculateBasicFormFan(Pack[] packs, CalculateParam calculateParam, WinFlag winFlag, int[] fanTable)
        {
            Pack pairPack = new Pack(0, PackType.Pair);
            Pack[] chowPacks = new Pack[4];
            Pack[] pungPacks = new Pack[4];
            int chowCnt = 0;
            int pungCnt = 0;
            for (int i = 0; i < 5; i++)
            {
                switch (packs[i].Type)
                {
                    case PackType.Chow: chowPacks[chowCnt++] = packs[i]; break;
                    case PackType.Pung:
                    case PackType.Kong: pungPacks[pungCnt++] = packs[i]; break;
                    case PackType.Pair: pairPack = packs[i]; break;
                }
            }

            if (pairPack.Type == PackType.Pair && chowCnt + pungCnt == 4)
            {
                int winTile = calculateParam.WinTile;

                // 根据和牌标记调整——涉及番种：和绝张、妙手回春、海底捞月、自摸
                AdjustByWinFlag(winFlag, fanTable);

                // 点和的牌张，如果不能解释为顺子中的一张，那么将其解释为刻子，并标记这个刻子为明刻
                if ((winFlag & WinFlag.SelfDrawn) == 0)
                {
                    // 和牌不能解释为顺子中的一张
                    if (!chowPacks.Any(chowPack => !chowPack.IsMelded &&
                        (chowPack.Tile - 1 == winTile || chowPack.Tile == winTile || chowPack.Tile + 1 == winTile)))
                    {
                        for (int i = 0; i < pungCnt; i++)
                        {
                            if (pungPacks[i].Tile == winTile && !pungPacks[i].IsMelded)
                            {
                                pungPacks[i] = new Pack(pungPacks[i].Tile, pungPacks[i].Type, true); // 标记为明副露
                            }
                        }
                    }
                }

                if (pungCnt > 0) // 有刻子
                {
                    CalculateKongs(pungPacks, pungCnt, fanTable);
                }

                switch (chowCnt)
                {
                    case 4: // 4组顺子
                        // 检测三色/一色双龙会
                        if (IsThreeSuitedTerminalChows(chowPacks, pairPack))
                        {
                            fanTable[(int)FanType.ThreeSuitedTerminalChows] = 1;
                            break;
                        }
                        if (IsPureTerminalChows(chowPacks, pairPack))
                        {
                            fanTable[(int)FanType.PureTerminalChows] = 1;
                            break;
                        }

                        int[] midTiles = new int[4];
                        midTiles[0] = chowPacks[0].Tile;
                        midTiles[1] = chowPacks[1].Tile;
                        midTiles[2] = chowPacks[2].Tile;
                        midTiles[3] = chowPacks[3].Tile;
                        Array.Sort(midTiles);

                        Calculate4Chows(midTiles, fanTable);
                        break;
                    case 3: // 3组顺子+1组刻子
                        int[] midTiles2 = new int[3];
                        midTiles2[0] = chowPacks[0].Tile;
                        midTiles2[1] = chowPacks[1].Tile;
                        midTiles2[2] = chowPacks[2].Tile;
                        Array.Sort(midTiles2);

                        Calculate3Chows(midTiles2, fanTable);
                        break;
                    case 2: // 2组顺子+2组刻子
                        int[] midTilesChow = new int[2];
                        midTilesChow[0] = chowPacks[0].Tile;
                        midTilesChow[1] = chowPacks[1].Tile;

                        int[] midTilesPung = new int[2];
                        midTilesPung[0] = pungPacks[0].Tile;
                        midTilesPung[1] = pungPacks[1].Tile;

                        Calculate2ChowsUnordered(midTilesChow, fanTable);
                        Calculate2PungsUnordered(midTilesPung, fanTable);
                        break;
                    case 1: // 1组顺子+3组刻子
                        int[] midTiles3 = new int[3];
                        midTiles3[0] = pungPacks[0].Tile;
                        midTiles3[1] = pungPacks[1].Tile;
                        midTiles3[2] = pungPacks[2].Tile;
                        Array.Sort(midTiles3);

                        Calculate3Pungs(midTiles3, fanTable);
                        break;
                    case 0: // 4组刻子
                        int[] midTiles4 = new int[4];
                        midTiles4[0] = pungPacks[0].Tile;
                        midTiles4[1] = pungPacks[1].Tile;
                        midTiles4[2] = pungPacks[2].Tile;
                        midTiles4[3] = pungPacks[3].Tile;
                        Array.Sort(midTiles4);

                        Calculate4Pungs(midTiles4, fanTable);
                        break;
                }

                int fixedCnt = calculateParam.HandTiles.PackCount;
                int[] standingTiles = calculateParam.HandTiles.StandingTiles;
                int standingCnt = calculateParam.HandTiles.TileCount;
                Wind prevalentWind = calculateParam.PrevalentWind;
                Wind seatWind = calculateParam.SeatWind;

                bool heavenWin = false;

                // 九莲宝灯
                if (!heavenWin && standingCnt == 13)
                {
                    if (IsNineGates(standingTiles))
                    {
                        fanTable[(int)FanType.NineGates] = 1;
                    }
                }

                // 根据和牌方式调整——涉及番种：不求人、全求人
                AdjustBySelfDrawn(packs, fixedCnt, (winFlag & WinFlag.SelfDrawn) != 0, fanTable);
                // 根据雀头调整——涉及番种：平和、小三元、小四喜
                AdjustByPairTile(pairPack.Tile, chowCnt, fanTable);
                // 根据牌组特征调整——涉及番种：全带幺、全带五、全双刻
                AdjustByPacksTraits(packs, fanTable);

                int[] tiles = new int[18];
                Array.Copy(standingTiles, tiles, standingCnt);
                int tileCnt = PacksToTiles(packs, fixedCnt, tiles, standingCnt);
                tiles[tileCnt++] = winTile;

                // 根据花色调整——涉及番种：无字、缺一门、混一色、清一色、五门齐
                AdjustBySuits(tiles, tileCnt, fanTable);
                // 根据牌特性调整——涉及番种：断幺、推不倒、绿一色、字一色、清幺九、混幺九
                AdjustByTilesTraits(tiles, tileCnt, fanTable);
                // 根据数牌的范围调整——涉及番种：大于五、小于五、全大、全中、全小
                AdjustByRankRange(tiles, tileCnt, fanTable);
                // 四归一调整
                AdjustByTilesHog(tiles, tileCnt, fanTable);

                if (!heavenWin)
                {
                    // 根据听牌方式调整——涉及番种：边张、嵌张、单钓将
                    AdjustByWaitingForm(packs, 5 - fixedCnt, standingTiles, standingCnt, winTile, fanTable);
                }

                // 统一调整一些不计的
                AdjustFanTable(fanTable);

                // 调整圈风刻、门风刻（大四喜不计圈风刻、门风刻）
                if (fanTable[(int)FanType.BigFourWinds] == 0)
                {
                    for (int i = 0; i < pungCnt; i++)
                    {
                        int tile = pungPacks[i].Tile;
                        if (IsWinds(tile))
                        {
                            AdjustByWinds(tile, prevalentWind, seatWind, fanTable);
                        }
                    }
                }

                // 如果什么番都没有，则计为无番和
                if (fanTable.All(f => f == 0))
                {
                    fanTable[(int)FanType.ChickenHand] = 1;
                }
            }
        }

        // “组合龙+面子+雀头”和型算番
        private static bool CalculateKnittedStraightFan(CalculateParam calculateParam, WinFlag winFlag, int[] fanTable)
        {
            HandTiles handTiles = calculateParam.HandTiles;
            int winTile = calculateParam.WinTile;
            Wind prevalentWind = calculateParam.PrevalentWind;
            Wind seatWind = calculateParam.SeatWind;

            int fixedCnt = handTiles.PackCount;
            if (fixedCnt > 1)
            {
                return false;
            }

            Pack[] fixedPacks = handTiles.FixedPacks;
            int standingCnt = handTiles.TileCount;

            // 对立牌和和牌的种类进行打表
            int[] cntTable = new int[136];
            MapTiles(handTiles.StandingTiles, standingCnt, cntTable);
            cntTable[winTile]++;

            // 匹配组合龙
            int[] matchedSeq = StandardKnittedStraight.FirstOrDefault(seq =>
                seq.All(t => cntTable[t] > 0));

            if (matchedSeq == null)
            {
                return false;
            }

            // 剔除组合龙
            foreach (int t in matchedSeq)
            {
                cntTable[t]--;
            }

            // 按基本和型划分
            DivisionResult result = new DivisionResult();
            Division workDivision = new Division();
            Array.Clear(workDivision.Packs, 0, 5);

            // 此处逻辑为：将组合龙9张牌当作是已经完成的3组面子，空出0 1 2下标处的3组
            // 如果第4组是副露的，将其放在下标3处
            // 然后按基本和型从从fixed_cnt + 3开始递归
            // 划分后的结果，下标3处为第四组面子，下标4处为雀头
            if (fixedCnt == 1)
            {
                workDivision.Packs[3] = fixedPacks[0];
            }
            int[] cntTableCopy = (int[])cntTable.Clone();
            if (!DivideRecursively(cntTableCopy, fixedCnt + 3, 0, workDivision, result))
            {
                return false;
            }
            if (result.Count != 1)
            {
                return false;
            }

            Pack[] packs = result.Divisions[0].Packs; // packs的0 1 2下标都是没用的

            // 标记番
            fanTable[(int)FanType.KnittedStraight] = 1; // 组合龙
            if (packs[3].Type == PackType.Chow) // 第4组是顺子
            {
                if (IsNumberedSuitQuick(packs[4].Tile))
                {
                    fanTable[(int)FanType.AllChows] = 1; // 雀头是数牌时，为平和
                }
            }
            else
            {
                CalculateKongs(new Pack[] { packs[3] }, 1, fanTable);
            }

            AdjustByWinFlag(winFlag, fanTable);
            // 门前清（暗杠不影响）
            if (fixedCnt == 0 || (packs[3].Type == PackType.Kong && !packs[3].IsMelded))
            {
                if ((winFlag & WinFlag.SelfDrawn) != 0)
                {
                    fanTable[(int)FanType.FullyConcealedHand] = 1;
                }
                else
                {
                    fanTable[(int)FanType.ConcealedHand] = 1;
                }
            }

            // 还原牌
            int[] tiles = new int[15];
            Array.Copy(matchedSeq, tiles, 9); // 组合龙的部分
            int tileCnt = PacksToTiles(new Pack[] { packs[3], packs[4] }, 2, tiles, 9); // 一组面子+一对雀头 最多6张牌
            //tileCnt += 9;

            // 根据花色调整——涉及番种：无字、缺一门、混一色、清一色、五门齐
            AdjustBySuits(tiles, tileCnt, fanTable);
            // 四归一调整
            AdjustByTilesHog(tiles, tileCnt, fanTable);

            // 和牌张是组合龙范围的牌，不计边张、嵌张、单钓将
            if (!matchedSeq.Contains(winTile))
            {
                if (fixedCnt == 0) // 门清的牌有可能存在边张、嵌张、单钓将
                {
                    // 将除去组合龙的部分恢复成牌
                    cntTable[winTile]--;
                    int[] temp = new int[4];
                    int cnt = TableToTiles(cntTable, temp, 4);

                    // 根据听牌方式调整——涉及番种：边张、嵌张、单钓将
                    AdjustByWaitingForm(packs, 2, temp, cnt, winTile, fanTable);
                }
                else
                {
                    // 非门清状态如果听牌不在组合龙范围内，必然是单钓将
                    fanTable[(int)FanType.SingleWait] = 1;
                }
            }

            // 统一调整一些不计的
            AdjustFanTable(fanTable);

            // 调整圈风刻、门风刻
            int tile = packs[3].Tile;
            if (IsWinds(tile))
            {
                AdjustByWinds(tile, prevalentWind, seatWind, fanTable);
            }

            return true;
        }

        // 十三幺
        private static bool IsThirteenOrphans(int[] tiles)
        {
            return tiles.All(t => IsTerminalOrHonor(t)) &&
                   StandardThirteenOrphans.All(t => tiles.Contains(t));
        }

        // 全不靠/七星不靠算番
        private static bool CalculateHonorsAndKnittedTiles(int[] standingTiles, int[] fanTable)
        {
            int honorIndex = Array.FindIndex(standingTiles, t => IsHonor(t));
            int numberedCnt = honorIndex;
            // 数牌张数大于9或者小于7必然不可能是全不靠
            if (numberedCnt > 9 || numberedCnt < 7)
            {
                return false;
            }

            // 匹配组合龙
            bool check = false;
            foreach (int[] list in StandardKnittedStraight)
            {
                int count = 0;
                foreach (int card in list)
                    if (Array.IndexOf(standingTiles, card) >= 0)
                        count++;
                for (int card = 0x41; card <= 0x47; card++)
                    if (Array.IndexOf(standingTiles, card) >= 0)
                        count++;
                if (count >= 14)
                    check = true;
            }
            if (!check)
            {
                return false;
            }

            if (numberedCnt == 7)
            {
                // 七种字牌齐，为七星不靠
                fanTable[(int)FanType.GreaterHonorsAndKnittedTiles] = 1;
                return true;
            }
            else
            {
                // 全不靠
                fanTable[(int)FanType.LesserHonorsAndKnittedTiles] = 1;
                if (numberedCnt == 9) // 有9张数牌，为带组合龙的全不靠
                {
                    fanTable[(int)FanType.KnittedStraight] = 1;
                }
                return true;
            }
        }

        // 特殊和型算番
        private static bool CalculateSpecialFormFan(int[] standingTiles, WinFlag winFlag, int[] fanTable)
        {
            // 七对
            if (standingTiles[0] == standingTiles[1] &&
                standingTiles[2] == standingTiles[3] &&
                standingTiles[4] == standingTiles[5] &&
                standingTiles[6] == standingTiles[7] &&
                standingTiles[8] == standingTiles[9] &&
                standingTiles[10] == standingTiles[11] &&
                standingTiles[12] == standingTiles[13])
            {
                if (IsNumberedSuitQuick(standingTiles[0]) &&
                    standingTiles[0] + 1 == standingTiles[2] &&
                    standingTiles[2] + 1 == standingTiles[4] &&
                    standingTiles[4] + 1 == standingTiles[6] &&
                    standingTiles[6] + 1 == standingTiles[8] &&
                    standingTiles[8] + 1 == standingTiles[10] &&
                    standingTiles[10] + 1 == standingTiles[12])
                {
                    // 连七对
                    fanTable[(int)FanType.SevenShiftedPairs] = 1;
                    AdjustByTilesTraits(standingTiles, 14, fanTable);
                }
                else
                {
                    // 普通七对
                    fanTable[(int)FanType.SevenPairs] = 1;

                    // 根据花色调整——涉及番种：无字、缺一门、混一色、清一色、五门齐
                    AdjustBySuits(standingTiles, 14, fanTable);
                    // 根据牌特性调整——涉及番种：断幺、推不倒、绿一色、字一色、清幺九、混幺九
                    AdjustByTilesTraits(standingTiles, 14, fanTable);
                    // 根据数牌的范围调整——涉及番种：大于五、小于五、全大、全中、全小
                    AdjustByRankRange(standingTiles, 14, fanTable);
                    // 四归一调整
                    AdjustByTilesHog(standingTiles, 14, fanTable);
                }
            }
            // 十三幺
            else if (IsThirteenOrphans(standingTiles))
            {
                fanTable[(int)FanType.ThirteenOrphans] = 1;
            }
            // 全不靠/七星不靠
            else if (CalculateHonorsAndKnittedTiles(standingTiles, fanTable))
            {
            }
            else
            {
                return false;
            }

            AdjustByWinFlag(winFlag, fanTable);
            // 统一调整一些不计的，根据风调整就没必要了，这些特殊和型都没有面子，不存在圈风刻、门风刻
            AdjustFanTable(fanTable);

            return true;
        }

        // 从番表计算番数
        private static int GetFanByTable(int[] fanTable)
        {
            int fan = 0;
            for (int i = 1; i < 83; i++)
            {
                if (fanTable[i] > 0)
                {
                    fan += FanValueTable[i] * fanTable[i];
                }
            }
            return fan;
        }

        // 判断立牌是否包含和牌
        private static bool IsStandingTilesContainsWinTile(int[] standingTiles, int standingCnt, int winTile)
        {
            return standingTiles.Take(standingCnt).Any(t => t == winTile);
        }

        // 统计和牌在副露牌组中出现的张数
        private static int CountWinTileInFixedPacks(Pack[] fixedPacks, int fixedCnt, int winTile)
        {
            int[] cntTable = new int[136];
            for (int i = 0; i < fixedCnt; i++)
            {
                Pack pack = fixedPacks[i];
                int tile = pack.Tile;
                switch (pack.Type)
                {
                    case PackType.Chow: cntTable[tile - 1]++; cntTable[tile]++; cntTable[tile + 1]++; break;
                    case PackType.Pung: cntTable[tile] += 3; break;
                    case PackType.Kong: cntTable[tile] += 4; break;
                }
            }
            return cntTable[winTile];
        }

        // 判断副露牌组是否包含杠
        private static bool IsFixedPacksContainsKong(Pack[] fixedPacks, int fixedCnt)
        {
            return fixedPacks.Take(fixedCnt).Any(pack => pack.Type == PackType.Kong);
        }

        // 检查算番的输入是否合法
        private static int CheckCalculatorInput(HandTiles handTiles, int winTile)
        {
            // 打表
            int[] cntTable = new int[136];
            if (!MapHandTiles(handTiles, cntTable))
            {
                return 1; // ERROR_WRONG_TILES_COUNT
            }
            if (winTile != 0)
            {
                cntTable[winTile]++;
            }

            // 如果某张牌超过4
            if (cntTable.Any(c => c > 4))
            {
                return 2; // ERROR_TILE_COUNT_GREATER_THAN_4
            }

            return 0;
        }

        // 算番主方法
        public static int CalculateFan(HandTiles handTiles, int winTile, WinFlag winFlag, Wind prevalentWind, Wind seatWind, int flowerCount, out string fan)
        {
            CalculateParam calculateParam = new CalculateParam
            {
                HandTiles = handTiles,
                WinTile = winTile,
                WinFlag = winFlag,
                PrevalentWind = prevalentWind,
                SeatWind = seatWind,
                FlowerCount = flowerCount
            };

            int ret = CheckCalculatorInput(handTiles, winTile);
            if (ret != 0)
            {
                fan = "";
                return 0;
            }

            int[] standingTiles = handTiles.StandingTiles;
            int standingCnt = handTiles.TileCount;

            // 校正和牌标记
            // 如果立牌包含和牌，则必然不是和绝张
            bool standingTilesContainsWinTile = IsStandingTilesContainsWinTile(standingTiles, standingCnt, winTile);
            if (standingTilesContainsWinTile)
            {
                winFlag &= ~WinFlag._4thTile;
            }

            // 如果和牌在副露中出现3张，则必然为和绝张
            int winTileInFixedPacks = CountWinTileInFixedPacks(handTiles.FixedPacks, handTiles.PackCount, winTile);
            if (winTileInFixedPacks == 3)
            {
                winFlag |= WinFlag._4thTile;
            }

            // 附加杠标记
            if ((winFlag & WinFlag.AboutKong) != 0)
            {
                if ((winFlag & WinFlag.SelfDrawn) != 0) // 自摸
                {
                    // 如果手牌没有杠，则必然不是杠上开花
                    if (!IsFixedPacksContainsKong(handTiles.FixedPacks, handTiles.PackCount))
                    {
                        winFlag &= ~WinFlag.AboutKong;
                    }
                }
                else // 点和
                {
                    // 如果和牌在手牌范围内出现过，则必然不是抢杠和
                    if (winTileInFixedPacks > 0 || standingTilesContainsWinTile)
                    {
                        winFlag &= ~WinFlag.AboutKong;
                    }
                }
            }

            // 合并立牌与和牌，并排序，最多为14张
            int[] combinedTiles = new int[standingCnt + 1];
            Array.Copy(standingTiles, combinedTiles, standingCnt);
            combinedTiles[standingCnt] = winTile;
            Array.Sort(combinedTiles);
            standingTiles = combinedTiles;

            // 最大番标记
            int maxFan = 0;
            int[] selectedFanTable = new int[83];

            // 特殊和型的番
            int[] specialFanTable = new int[83];

            // 先判断各种特殊和型
            if (handTiles.PackCount == 0) // 门清状态，有可能是基本和型组合龙
            {
                if (CalculateKnittedStraightFan(calculateParam, winFlag, specialFanTable))
                {
                    maxFan = GetFanByTable(specialFanTable);
                    Array.Copy(specialFanTable, selectedFanTable, 83);
                }
                else if (CalculateSpecialFormFan(combinedTiles, winFlag, specialFanTable))
                {
                    maxFan = GetFanByTable(specialFanTable);
                    Array.Copy(specialFanTable, selectedFanTable, 83);
                }
            }
            else if (handTiles.PackCount == 1) // 1副露状态，有可能是基本和型组合龙
            {
                if (CalculateKnittedStraightFan(calculateParam, winFlag, specialFanTable))
                {
                    maxFan = GetFanByTable(specialFanTable);
                    Array.Copy(specialFanTable, selectedFanTable, 83);
                }
            }

            // 无法构成特殊和型或者为七对
            if (maxFan == 0 || specialFanTable[(int)FanType.SevenPairs] > 0)
            {
                // 划分
                DivisionResult result = new DivisionResult();
                if (DivideWinHand(standingTiles, handTiles.FixedPacks, handTiles.PackCount, result))
                {
                    int[][] fanTables = new int[MAX_DIVISION_CNT][];
                    for (int i = 0; i < MAX_DIVISION_CNT; i++)
                    {
                        fanTables[i] = new int[83];
                    }

                    // 遍历各种划分方式，分别算番，找出最大的番的划分方式
                    for (int i = 0; i < result.Count; i++)
                    {
                        CalculateBasicFormFan(result.Divisions[i].Packs, calculateParam, winFlag, fanTables[i]);
                        int currentFan = GetFanByTable(fanTables[i]);
                        if (currentFan > maxFan)
                        {
                            maxFan = currentFan;
                            Array.Copy(fanTables[i], selectedFanTable, 83);
                        }
                    }
                }
            }

            if (maxFan == 0)
            {
                fan = "";
                return 0; // ERROR_NOT_WIN
            }

            fan = "";
            for (int i = 0; i < selectedFanTable.Length; i++)
                if (selectedFanTable[i] > 0)
                    fan += ((FanType)i).ToString() + "*" + selectedFanTable[i] + " ";

            // 加花牌
            maxFan += calculateParam.FlowerCount;

            return maxFan;
        }
    }
}